﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Kokomo.Mirror.Ia32;
using Kokomo.PeInspector.Reflection;
using Kokomo.PeInspector.Native.Coding;
using Kokomo.Lexical.Expressions;

namespace Kokomo.PeInspector.Native
{
	class NativePseudocodeDocumentBuilder : CSharpDocumentBuilder
	{
		public NativePseudocodeDocumentBuilder(
			ICodeWriter writer
			)
			: base(writer, null, null)
		{
		}

		public void WriteProcedure(NativeProcedureInfo procedure)
		{
			if (procedure == null) throw new ArgumentNullException("procedure");

			Ia32Disassembly disassembly = procedure.Disassemble();
			NativeProcedureDecompilation decompilation = NativeDecompiler.Decompile(procedure);

			foreach (var statement in decompilation.Statements)
			{
				this.WriteStatement(statement);
			}
		}

		#region Statement writers
		protected override void DefineStatementWriters(IDictionary<Type, Action<Lexical.Coding.Statement>> writerTable)
		{
			base.DefineStatementWriters(writerTable);

			writerTable[typeof(InstructionStatement)] = CreateStatementWriterThunk<InstructionStatement>(this.WriteInstructionStatement);
			writerTable[typeof(CallStatement)] = CreateStatementWriterThunk<CallStatement>(this.WriteCallStatement);
		}
		private void WriteInstructionStatement(InstructionStatement instructionStatement)
		{
			this.WriteKeywordText(instructionStatement.Instruction.OpCode.Mnemonic.Name);

			Expression[] arguments = instructionStatement.Arguments;
			if (arguments.Length > 0)
			{
				this.WriteText(" ");
				this.WriteExpression(arguments[0]);
				for (int i = 1; i < instructionStatement.Arguments.Length; i++)
				{
					this.Writer.WriteText(", ", CodeTextType.Operator);
					this.WriteExpression(arguments[i]);
				}
			}
		}
		private void WriteCallStatement(CallStatement callStatement)
		{
			this.WriteKeywordsWithTrailingSpace("call");
			this.WriteExpression(callStatement.Target);
		}
		#endregion
		#region Value writers
		protected override void WriteSignedNumeric(long value)
		{
			this.WriteText(string.Format("0{0:X}h", value));
		}
		protected override void WriteUnsignedNumeric(ulong value)
		{
			this.WriteText(string.Format("0{0:X}h", value));
		}
		#endregion
	}
}
